﻿using System;
using System.Collections.Generic;
using System.Linq;
using System.Text;
using AdaptiveDeNet.CSV;
using AdaptiveDeNet.RAND;

namespace AdaptiveDeNet.DifferentialEvolution
{
    public partial class Optimizer
    {
        /// <summary>
        /// DE/rand/1/bin, tries to find global minimum point of given function
        /// </summary>
        /// <param name="F">Scale factor</param>
        /// <param name="Cr">Crossover probability,</param>
        /// <param name="lowerBound">Lower bound vector</param>
        /// <param name="upperBound">Upper bound vector</param>
        /// <param name="maxEvals">maximum evaluations of objective function</param>
        /// <param name="dim">dimension of problem</param>
        /// <param name="N">size of population</param>
        /// <param name="myEpps">small nonnegative value, when (Fmax - Fmin) is smaller then myEpps the termination criteria are satisfied</param>
        /// <returns>Result</returns>
        public Result de_rand_1_bin(double F, double Cr, double[] lowerBound,
           double[] upperBound, int maxEvals, int dim, int N, double myEpps)
        {
            this.nRand = new Rand();

            double Fmin = double.MaxValue;
            double Fmax = double.MinValue;

            //deklarace hlavni populace
            Member[] mainPop = new Member[N];
            Member[] trials = new Member[N];

            Member trial;

            //inicializace
            // x = rand(0,1) * (uB - lB) + lb

            double[] uBlBDiff = new double[dim];

            for (int i = 0; i < dim; i++)
            {
                uBlBDiff[i] = upperBound[i] - lowerBound[i];
            }

            for (int i = 0; i < N; i++)
            {
                Member newMember = new Member(dim);

                // pro kazdou dimenzi
                for (byte j = 0; j < dim; j++)
                {
                    newMember.Vector[j] = nRand.NextDouble() * uBlBDiff[j] + lowerBound[j];
                }

                //evaluace
                newMember.FEval = fEval.Feval(newMember.Vector);
                // zjisteni min. hodnoty  funkce v nulte populaci
                if (newMember.FEval < Fmin) Fmin = newMember.FEval;

                mainPop[i] = newMember;
            }

            Fmax = Common.Common.FindMax(mainPop);
            int popEvals = N;

            //main loop
            while (popEvals < maxEvals && (Fmax - Fmin) > myEpps)
            {
                //vygenerovani  
                for (int i = 0; i < N; i++)
                {

                    trial = new Member(dim);

                    int r0Index = this.nRand.Next(N);
                    int r1Index = this.nRand.Next(N);
                    int r2Index = this.nRand.Next(N);

                    while (i == r0Index) r0Index = this.nRand.Next(N);

                    while (i == r1Index || r0Index == r1Index) r1Index = this.nRand.Next(N);

                    while (i == r2Index || r0Index == r2Index || r1Index == r2Index) r2Index = this.nRand.Next(N);

                    int jrand = this.nRand.Next(dim);

                    //pro kazdou dimenzi
                    for (int j = 0; j < dim; j++)
                    {
                        //krizeni
                        if (this.nRand.NextDouble() <= Cr || j == jrand)
                        {
                            // 
                            // x = r0 + F * (r1 - r2)
                            double newVal = mainPop[r0Index].Vector[j] + F * (mainPop[r1Index].Vector[j] - mainPop[r2Index].Vector[j]);

                            if (newVal < lowerBound[j] || newVal > upperBound[j])
                            {
                                newVal = Common.Common.Zrdcad(newVal, lowerBound[j], upperBound[j]);
                            }

                            trial.Vector[j] = newVal;
                        }
                        else
                        {
                            trial.Vector[j] = mainPop[i].Vector[j];
                        }
                    }

                    //evaluace  fce
                    trial.FEval = this.fEval.Feval(trial.Vector);
                    trials[i] = trial;
                    popEvals++;
                }

                //selekce
                for (int i = 0; i < N; i++)
                {
                    if (trials[i].FEval <= mainPop[i].FEval)
                    {
                        mainPop[i] = trials[i];
                        if (trials[i].FEval < Fmin) Fmin = trials[i].FEval;
                    }
                }

                Fmax = Common.Common.FindMax(mainPop);
            }

            Result result = new Result();

            result.FuncEvals = popEvals;
            result.Fmin = Fmin;
            result.Point = Common.Common.GetBestMember(mainPop);

            return result;
        }

        /// <summary>
        /// DE/rand/1/bin, tries to find global minimum point of given function
        /// </summary>
        /// <param name="function">objective function</param>
        /// <param name="F">Scale factor</param>
        /// <param name="Cr">Crossover probability,</param>
        /// <param name="lowerBound">Lower bound vector</param>
        /// <param name="upperBound">Upper bound vector</param>
        /// <param name="maxEvals">maximum evaluations of objective function</param>
        /// <param name="dim">dimension of problem</param>
        /// <param name="N">size of population</param>
        /// <param name="myEpps">small nonnegative value, when (Fmax - Fmin) is smaller then myEpps the termination criteria are satisfied</param>
        /// <returns>Result</returns>
        public Result de_rand_1_bin(Func<double[], double> function, double F, double Cr, double[] lowerBound,
           double[] upperBound, int maxEvals, int dim, int N, double myEpps)
        {
            this.nRand = new Rand();

            double Fmin = double.MaxValue;
            double Fmax = double.MinValue;

            //deklarace hlavni populace
            Member[] mainPop = new Member[N];
            Member[] trials = new Member[N];

            Member trial;

            //inicializace
            // x = rand(0,1) * (uB - lB) + lb

            double[] uBlBDiff = new double[dim];

            for (int i = 0; i < dim; i++)
            {
                uBlBDiff[i] = upperBound[i] - lowerBound[i];
            }

            for (int i = 0; i < N; i++)
            {
                Member newMember = new Member(dim);

                // pro kazdou dimenzi
                for (byte j = 0; j < dim; j++)
                {
                    newMember.Vector[j] = nRand.NextDouble() * uBlBDiff[j] + lowerBound[j];
                }

                //evaluace
                newMember.FEval = function.Invoke(newMember.Vector);
                // zjisteni min. hodnoty  funkce v nulte populaci
                if (newMember.FEval < Fmin) Fmin = newMember.FEval;

                mainPop[i] = newMember;
            }

            Fmax = Common.Common.FindMax(mainPop);
            int popEvals = N;

            //main loop
            while (popEvals < maxEvals && (Fmax - Fmin) > myEpps)
            {
                //vygenerovani  
                for (int i = 0; i < N; i++)
                {

                    trial = new Member(dim);

                    int r0Index = this.nRand.Next(N);
                    int r1Index = this.nRand.Next(N);
                    int r2Index = this.nRand.Next(N);

                    while (i == r0Index) r0Index = this.nRand.Next(N);

                    while (i == r1Index || r0Index == r1Index) r1Index = this.nRand.Next(N);

                    while (i == r2Index || r0Index == r2Index || r1Index == r2Index) r2Index = this.nRand.Next(N);

                    int jrand = this.nRand.Next(dim);

                    //pro kazdou dimenzi
                    for (int j = 0; j < dim; j++)
                    {
                        //krizeni
                        if (this.nRand.NextDouble() <= Cr || j == jrand)
                        {
                            // 
                            // x = r0 + F * (r1 - r2)
                            double newVal = mainPop[r0Index].Vector[j] + F * (mainPop[r1Index].Vector[j] - mainPop[r2Index].Vector[j]);

                            if (newVal < lowerBound[j] || newVal > upperBound[j])
                            {
                                newVal = Common.Common.Zrdcad(newVal, lowerBound[j], upperBound[j]);
                            }

                            trial.Vector[j] = newVal;
                        }
                        else
                        {
                            trial.Vector[j] = mainPop[i].Vector[j];
                        }
                    }

                    //evaluace  fce
                    trial.FEval = function.Invoke(trial.Vector);
                    trials[i] = trial;
                    popEvals++;
                }

                //selekce
                for (int i = 0; i < N; i++)
                {
                    if (trials[i].FEval <= mainPop[i].FEval)
                    {
                        mainPop[i] = trials[i];
                        if (trials[i].FEval < Fmin) Fmin = trials[i].FEval;
                    }
                }

                Fmax = Common.Common.FindMax(mainPop);
            }

            Result result = new Result();

            result.FuncEvals = popEvals;
            result.Fmin = Fmin;
            result.Point = Common.Common.GetBestMember(mainPop);

            return result;
        }

         /// <summary>
        /// DE/rand/1/bin, tries to find global minimum point of given function
        /// </summary>
        /// <param name="F">Scale factor</param>
        /// <param name="Cr">Crossover probability,</param>
        /// <param name="lowerBound">Lower bound vector</param>
        /// <param name="upperBound">Upper bound vector</param>
        /// <param name="maxEvals">maximum evaluations of objective function</param>
        /// <param name="dim">dimension of problem</param>
        /// <param name="N">size of population</param>
        /// <param name="myEpps">small nonnegative value, when (Fmax - Fmin) is smaller then myEpps the termination criteria are satisfied</param>
        /// <returns>Result</returns>
        public CEC14Result de_rand_1_bin(double F, double Cr, double[] lowerBound,
           double[] upperBound, int maxEvals, int dim, int N, double myEpps, Queue<double> FESMultipliers, double fX)
        {
            CEC14Result result = new CEC14Result();
            this.nRand = new Rand();

            double Fmin = double.MaxValue;
            double Fmax = double.MinValue;

            //deklarace hlavni populace
            Member[] mainPop = new Member[N];
            Member[] trials = new Member[N];

            Member trial;

            //inicializace
            // x = rand(0,1) * (uB - lB) + lb

            double[] uBlBDiff = new double[dim];

            for (int i = 0; i < dim; i++)
            {
                uBlBDiff[i] = upperBound[i] - lowerBound[i];
            }

            for (int i = 0; i < N; i++)
            {
                Member newMember = new Member(dim);

                // pro kazdou dimenzi
                for (byte j = 0; j < dim; j++)
                {
                    newMember.Vector[j] = nRand.NextDouble() * uBlBDiff[j] + lowerBound[j];
                }

                //evaluace
                newMember.FEval = fEval.Feval(newMember.Vector);
                // zjisteni min. hodnoty  funkce v nulte populaci
                if (newMember.FEval < Fmin) Fmin = newMember.FEval;

                mainPop[i] = newMember;
            }

            Fmax = Common.Common.FindMax(mainPop);
            int popEvals = N;

            //main loop
            while (popEvals < maxEvals)
            {
                //vygenerovani  
                for (int i = 0; i < N; i++)
                {

                    trial = new Member(dim);

                    int r0Index = this.nRand.Next(N);
                    int r1Index = this.nRand.Next(N);
                    int r2Index = this.nRand.Next(N);

                    while (i == r0Index) r0Index = this.nRand.Next(N);

                    while (i == r1Index || r0Index == r1Index) r1Index = this.nRand.Next(N);

                    while (i == r2Index || r0Index == r2Index || r1Index == r2Index) r2Index = this.nRand.Next(N);

                    int jrand = this.nRand.Next(dim);

                    //pro kazdou dimenzi
                    for (int j = 0; j < dim; j++)
                    {
                        //krizeni
                        if (this.nRand.NextDouble() <= Cr || j == jrand)
                        {
                            // 
                            // x = r0 + F * (r1 - r2)
                            double newVal = mainPop[r0Index].Vector[j] + F * (mainPop[r1Index].Vector[j] - mainPop[r2Index].Vector[j]);

                            if (newVal < lowerBound[j] || newVal > upperBound[j])
                            {
                                newVal = Common.Common.Zrdcad(newVal, lowerBound[j], upperBound[j]);
                            }

                            trial.Vector[j] = newVal;
                        }
                        else
                        {
                            trial.Vector[j] = mainPop[i].Vector[j];
                        }
                    }

                    //evaluace  fce
                    trial.FEval = this.fEval.Feval(trial.Vector);
                    trials[i] = trial;
                    popEvals++;
                }
             

                //selekce
                for (int i = 0; i < N; i++)
                {
                    if (trials[i].FEval <= mainPop[i].FEval)
                    {
                        mainPop[i] = trials[i];
                        if (trials[i].FEval < Fmin) Fmin = trials[i].FEval;
                    }
                }

                if (popEvals == (FESMultipliers.Peek() * maxEvals))
                {
                    result.Records.Add(Fmin - fX);
                    FESMultipliers.Dequeue();
                }

                Fmax = Common.Common.FindMax(mainPop);
            }

            result.FuncEvals = popEvals;


            return result;
        }
    

    }
}
